package netty.rpc.framework.core.netty.spring;

import lombok.extern.slf4j.Slf4j;
import netty.rpc.framework.core.netty.annotation.RPCReference;
import netty.rpc.framework.core.netty.annotation.RPCService;
import netty.rpc.framework.core.netty.consumer.ClientTransport;
import netty.rpc.framework.core.netty.consumer.ConsumerProxy;
import netty.rpc.framework.core.netty.factory.SingletonFactory;
import netty.rpc.framework.core.netty.registry.ServiceRegistry;
import netty.rpc.framework.core.netty.registry.impl.ZKServiceRegistryImpl;
import netty.rpc.framework.core.netty.transport.server.ServiceHandler;
import netty.rpc.framework.core.netty.transport.server.ServiceProvider;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.net.Inet4Address;
import java.net.UnknownHostException;

/**
 * SpringBoot 动态注入bean接口
 * 通过对RPCService的bean对象生成前进行增强，添加自动服务注册功能
 * 通过对RPCReference的bean属性生成后进行增强，为bean对象赋予可访问服务的一个动态代理实现实例
 *
 */
@Slf4j
@Component
public class RPCBeanPostProcessor implements BeanPostProcessor {


    private final ServiceRegistry serviceRegistry = new ZKServiceRegistryImpl();

    private final ClientTransport clientTransport = SingletonFactory.getInstance(ClientTransport.class);

    private final ServiceProvider serviceProvider = SingletonFactory.getInstance(ServiceProvider.class);

    @Value("${rpc.server.port:111}")
    private int rpcPort ;

    /**
     * 实现生成bean的前置方法，用于服务端RPCService对象组件生成时服务自动注册
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        Class<?> clazz = bean.getClass();
        if(clazz.isAnnotationPresent(RPCService.class)){
            log.info("[{}] is annotated with [{}]",clazz.getName(),RPCService.class.getCanonicalName());
            //RPCService rpcService = clazz.getAnnotation(RPCService.class);
            Class<?> serviceInterface = clazz.getInterfaces()[0];
            //向远程服务中心注册服务操作  url  ip:port
            serviceRegistry.registerService(serviceInterface.getCanonicalName(), getServiceUrl(rpcPort));
            //为本进程添加服务节点
            serviceProvider.addService(serviceInterface.getCanonicalName(),bean);

            log.info("服务- {} -已经注册成功",serviceInterface.getCanonicalName()) ;
        }
        return bean;
    }


    /**
     * 实现生成bean的后置方法，用于客户端RPCReference对象的动态代理对象生成
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        Class<?> clazz = bean.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field field : declaredFields){
            RPCReference rpcReference = field.getAnnotation(RPCReference.class);
            if(rpcReference != null ){
                log.info("操作 rpcReference"+field.toString());
                //动态代理客户端对象操作
                ConsumerProxy rpcClientServiceProxy = new ConsumerProxy(clientTransport);
                Object serviceBean = rpcClientServiceProxy.getServiceBean(field.getType());
                field.setAccessible(true);
                try {
                    field.set(bean,serviceBean);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        return bean;
    }

    private String getServiceUrl(int port){
        if(port <= 0){
            throw new IllegalArgumentException("套接字地址错误！");
        }
        StringBuilder builder = new StringBuilder();
        try {
            builder.append(Inet4Address.getLocalHost().getHostAddress());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        builder.append(":");
        builder.append(port);
        return builder.toString();
    }

}
